package org.su.mamamiya.web.utils;

import com.squareup.javapoet.AnnotationSpec;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterSpec;
import com.squareup.javapoet.TypeSpec;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.su.mamamiya.core.ast.DataType;
import org.su.mamamiya.core.ast.Param;
import org.su.mamamiya.web.annotation.Rpc;


import javax.lang.model.element.AnnotationValue;
import javax.lang.model.element.AnnotationValueVisitor;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.VariableElement;
import java.util.List;
import java.util.Map;

public class ProcessorHelper {


    public static void setMapping(TypeSpec.Builder method, Rpc src) {

        if (src == null || (ArrayUtils.isEmpty(src.url()) && ArrayUtils.isEmpty(src.method()))) {
            return;
        }

        AnnotationSpec.Builder asB = AnnotationSpec.builder(RequestMapping.class);

        if (src.url().length > 0) {
            int len = src.url().length;
            String[] args = new String[len];
            for (int i = 0; i < len; i++) {
                args[i] = src.url()[i];
                asB.addMember("value", "$S",  args[i]);
            }
        }
        if (src.method().length > 0) {
            for (RequestMethod rmm : src.method()) {
                asB.addMember("method", "$T.$L", RequestMethod.class, rmm);
            }
        }

        method.addAnnotation(asB.build());
    }

    public static boolean setMapping(MethodSpec.Builder method, RequestMapping src, String context) {
        if (src == null){ return false;}

        AnnotationSpec.Builder asB = AnnotationSpec.builder(RequestMapping.class);
        setStrings(asB, context, src.value(), "value");
        setStrings(asB, "", src.headers(), "headers");
        setStrings(asB, "", src.produces(), "produces");
        setStrings(asB, "", src.params(), "params");
        setStrings(asB, "", src.consumes(), "consumes");

        if (src.method().length > 0) {
            for (RequestMethod rmm : src.method())
                asB.addMember("method", "$T.$L", RequestMethod.class, rmm);
        }

        method.addAnnotation(asB.build());
        return true;
    }

    /**
     * 设置默认
     *
     * @param method
     * @param methods
     * @param context
     * @param urls
     * @return
     */
    public static boolean setDefaultMapping(ExecutableElement amethod, MethodSpec.Builder method, RequestMethod[] methods, String context, String urls) {
        AnnotationSpec.Builder asB = AnnotationSpec.builder(RequestMapping.class);
        setStrings(asB, context, new String[]{urls}, "value");
        if (amethod.getReturnType().toString().equals(String.class.getName())) {
            asB.addMember("produces", "$S", "text/html;charset=UTF-8");
        }

        if (methods.length > 0) {
            for (RequestMethod rmm : methods) {
                asB.addMember("method", "$T.$L", RequestMethod.class, rmm);
            }
        }

        method.addAnnotation(asB.build());
        return true;
    }

    /**
     * 得到方法参数
     * @param ve
     * @param name
     * @return
     */
    public static Param getParam(VariableElement ve, String name){
        Param param = new Param();
    if (ve.getAnnotation(RequestParam.class) != null){
        param.setParamType("query");
        param.setRequired(ve.getAnnotation(RequestParam.class).required());
        param.setExistAnnotation(true);
        return param;
    }
    if( ve.getAnnotation(RequestBody.class) != null){
        param.setParamType("body");
        param.setRequired(ve.getAnnotation(RequestBody.class).required());
        param.setExistAnnotation(true);
        return param;
    }
    if(ve.getAnnotation(PathVariable.class) != null) {
        param.setParamType("path");
        param.setRequired(ve.getAnnotation(PathVariable.class).required());
        param.setExistAnnotation(true);
        return param;
    }
    if(ve.getAnnotation(RequestHeader.class) != null) {
        param.setParamType("header");
        param.setRequired(ve.getAnnotation(RequestHeader.class).required());
        param.setExistAnnotation(true);
        return param;
    }

    AnnotationSpec.Builder asb = null;
    String bodyName = "Body";
    if (name.endsWith(bodyName)) {
        param.setParamType("body");
        asb = AnnotationSpec.builder(RequestBody.class);
        param.setParamAnnotationBuilder(asb);
        param.setExistAnnotation(false);
        return param;
    } else {
        String typeName = DataType.getDataType(ve);
        switch (typeName) {
            case "boolean":
            case "char":
            case "byte":
            case "short":
            case "int":
            case "float":
            case "double":
            case "long":
            case "String":
            case "string":
            case "date":
            case "biginteger":
            case "bigdecimal":
                param.setParamType("query");
                asb = AnnotationSpec.builder(RequestParam.class);
                asb.addMember("value", "$S", name);
                param.setParamAnnotationBuilder(asb);
                break;
            default:
                param.setParamType("form");
                break;
        }
        param.setExistAnnotation(false);
        return param;
    }
}

    /**
     * 设置方法参数
     * @param paramter
     * @param params
     * @return
     */
    public static boolean setDefaultPramater(ParameterSpec.Builder paramter, Param params) {
        if(params.isExistAnnotation()){
            return false;
        }
        if(params.getParamAnnotationBuilder() != null) {
            paramter.addAnnotation(params.getParamAnnotationBuilder().build());
        }
        if(params.isRequired() &&
                ("body".equals(params.getParamType()) ||
                        "form".equals(params.getParamType()))){
            AnnotationSpec.Builder vasb = AnnotationSpec.builder(Validated.class);
            paramter.addAnnotation(vasb.build());
        }
        return true;
    }

    /**
     * 设置参数，如果以Body 结尾的就用RequestBody注解，否者用RequestParam注解
     *
     * @param paramter
     * @param name
     * @return
     */
    public static boolean setDefaultPramater(VariableElement ve, ParameterSpec.Builder paramter, String name,Param param) {
        if (ve.getAnnotation(RequestParam.class) != null){
            param.setParamType("query");
            param.setRequired(ve.getAnnotation(RequestParam.class).required());
            return false;
        }
        if( ve.getAnnotation(RequestBody.class) != null){
            param.setParamType("body");
            param.setRequired(ve.getAnnotation(RequestBody.class).required());
            return false;
        }
          if(ve.getAnnotation(PathVariable.class) != null) {
              param.setParamType("path");
              param.setRequired(ve.getAnnotation(PathVariable.class).required());
            return false;
        }
        if(ve.getAnnotation(RequestHeader.class) != null) {
            param.setParamType("header");
            param.setRequired(ve.getAnnotation(RequestHeader.class).required());
            return false;
        }

        AnnotationSpec.Builder asb = null;
        String bodyName = "Body";
        if (name.endsWith(bodyName)) {
            param.setParamType("body");
            asb = AnnotationSpec.builder(RequestBody.class);
        } else {
            String typeName = DataType.getDataType(ve);
            switch (typeName) {
                case "boolean":
                case "char":
                case "byte":
                case "short":
                case "int":
                case "float":
                case "double":
                case "long":
                case "String":
                case "string":
                case "date":
                case "biginteger":
                case "bigdecimal":
                    param.setParamType("query");
                    asb = AnnotationSpec.builder(RequestParam.class);
                    asb.addMember("value", "$S", name);
                    if(!param.isRequired()){
                       asb.addMember("required","$L",false);
                    }
                    break;
                default:
                    param.setParamType("form");
                    break;
            }
        }
        if(asb != null) {
            paramter.addAnnotation(asb.build());
        }
        if(param.isRequired() &&
                ("body".equals(param.getParamType()) ||
        "form".equals(param.getParamType()))){
            AnnotationSpec.Builder vasb = AnnotationSpec.builder(Validated.class);
            paramter.addAnnotation(vasb.build());
        }
        return true;
    }

    public static boolean setMapping(MethodSpec.Builder method, GetMapping src, String context) {
        if (src == null){ return false;}

        AnnotationSpec.Builder asB = AnnotationSpec.builder(GetMapping.class);

        setStrings(asB, context, src.value(), "value");
        setStrings(asB, "", src.headers(), "headers");
        setStrings(asB, "", src.produces(), "produces");
        setStrings(asB, "", src.params(), "params");
        setStrings(asB, "", src.consumes(), "consumes");
        method.addAnnotation(asB.build());
        return true;
    }

    public static boolean setMapping(MethodSpec.Builder method, PostMapping src, String context) {

        if (src == null){ return false;}

        AnnotationSpec.Builder asB = AnnotationSpec.builder(PostMapping.class);

        setStrings(asB, context, src.value(), "value");
        setStrings(asB, "", src.headers(), "headers");
        setStrings(asB, "", src.produces(), "produces");
        setStrings(asB, "", src.params(), "params");
        setStrings(asB, "", src.consumes(), "consumes");

        method.addAnnotation(asB.build());
        return true;
    }

    public static boolean setMapping(MethodSpec.Builder method, DeleteMapping src, String context) {
        if (src == null){ return false;}

        AnnotationSpec.Builder asB = AnnotationSpec.builder(DeleteMapping.class);

        setStrings(asB, context, src.value(), "value");
        setStrings(asB, "", src.headers(), "headers");
        setStrings(asB, "", src.produces(), "produces");
        setStrings(asB, "", src.params(), "params");
        setStrings(asB, "", src.consumes(), "consumes");
        method.addAnnotation(asB.build());
        return true;
    }

    public static boolean setMapping(MethodSpec.Builder method, PutMapping src, String context) {
        if (src == null){ return false;}

        AnnotationSpec.Builder asB = AnnotationSpec.builder(PutMapping.class);

        setStrings(asB, context, src.value(), "value");
        setStrings(asB, "", src.headers(), "headers");
        setStrings(asB, "", src.produces(), "produces");
        setStrings(asB, "", src.params(), "params");
        setStrings(asB, "", src.consumes(), "consumes");
        method.addAnnotation(asB.build());
        return true;
    }


    private static void setStrings(AnnotationSpec.Builder asb, String context, String[] values, String name) {
        for (String value : values) {
            asb.addMember(name, "$S", context + value);
        }

    }


    public static String[] getUrlMapping(Rpc src, String context) {

        if (src == null || (ArrayUtils.isEmpty(src.url()) && ArrayUtils.isEmpty(src.method()))) {
            return new String[]{context};
        }
        AnnotationSpec.Builder asB = AnnotationSpec.builder(RequestMapping.class);
        String[] args = null;
        if (src.url().length > 0) {
            int len = src.url().length;
            args = new String[len];
            for (int i = 0; i < len; i++) {
                args[i] = context + src.url()[i];
            }
        }
        return args;
    }


    public static String setAnnotationValue(
            Map<ExecutableElement, AnnotationValue> values,
            String annotationFiledName, String context) {
        for (Map.Entry<ExecutableElement, AnnotationValue> entry : values
                .entrySet()) {
            if (entry.getKey().getSimpleName()
                    .contentEquals(annotationFiledName)) {

                AnnotationValue av = entry.getValue();
                entry.setValue(new AnnotationValue() {
                    @Override
                    public Object getValue() {
                        return context + av.getValue();
                    }

                    @Override
                    public <R, P> R accept(AnnotationValueVisitor<R, P> v, P p) {
                        return null;
                    }
                });

                return av.getValue().toString();
            }
        }
        return null;
    }


    /**
     * 比较方法是否一致
     * @param src
     * @param dest
     * @return
     */
    public static boolean methodEquals(ExecutableElement src, ExecutableElement dest) {
        if(!src.getSimpleName().toString().equals(dest.getSimpleName().toString())){
            return false;
        }
        boolean flag = false;
        List<? extends VariableElement> destTpes = dest.getParameters();
        List<? extends VariableElement> srcTpes = src.getParameters();
        int i = 0;
        if (destTpes.size() == srcTpes.size()) {
            if(destTpes.size() == 0){ return true;}
            for (VariableElement tpe : destTpes) {
                flag = tpe.asType().toString().equals(srcTpes.get(i++).asType().toString());
                if(!flag){
                    return false;
                }
            }
            return true;
        }else{
            return false;
        }

    }

}
